package com.htthorizon.neo4j.controller;

import cn.hutool.json.JSONArray;
import com.htthorizon.common.core.constant.Constants;
import com.htthorizon.common.core.utils.StringUtils;
import com.htthorizon.common.log.annotation.Log;
import com.htthorizon.common.log.enums.BusinessType;
import com.htthorizon.neo4j.entity.*;
import com.htthorizon.neo4j.repository.*;
import com.htthorizon.neo4j.service.RootServiceNeo4j;
import com.htthorizon.neo4j.vo.NodeVo;
import lombok.extern.slf4j.Slf4j;
import org.neo4j.driver.*;
import org.neo4j.driver.async.AsyncSession;
import org.neo4j.driver.types.Node;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import java.util.*;

/**
 * @author Brilliant
 */
@RestController
@RequestMapping("/neo4j")
@Slf4j
public class Neo4jController {

    @Autowired
    private RootServiceNeo4j rootServiceNeo4j;

    @Autowired
    private RootRepository rootRepository;

    @Autowired
    private RelationShipRepository relationShipRepository;

    @Autowired
    private RelationShip2Repository relationShip2Repository;

    @Autowired
    private RelationShip3Repository relationShip3Repository;
    @Autowired
    private RelationShip4Repository relationShip4Repository;

    @Autowired
    private RelationShip5Repository relationShip5Repository;


    @Autowired
    private ModelRepository modelRepository;

    @Autowired
    private BDRepository bdRepository;

    @Autowired
    private FieldRepository fieldRepository;

    @Autowired
    private UpstreamRepository upstreamRepository;

    @Autowired
    private UpstreamFieldRepository upstreamFieldRepository;


    @GetMapping("/all")
    @Log(title = "模型管理/图谱管理", businessType = BusinessType.QUERY)
    public NodeVo all(){
        Iterable<Root> all = rootRepository.findAll();
        Iterable<BD> all2 = bdRepository.findAll();
        Iterable<Model> all3 = modelRepository.findAll();
        List<Object> list = new ArrayList<>();
        for (Root root:all){
            list.add(root);
        }
        for (BD root:all2){
            list.add(root);
        }
        for (Model model:all3){
            list.add(model);
        }
        String s = this.node2Json(list);
        List<RelationShip> all1 = relationShipRepository.All();
        List<RelationShip2> all4 = relationShip2Repository.All();
        List<Object> list1 = new ArrayList<>();
        for (RelationShip relationShip:all1){
            list1.add(relationShip);
        }
        for (RelationShip2 relationShip:all4){
            list1.add(relationShip);
        }
        String s1 = this.relationShip2Json(list1);
        NodeVo nodeVo = new NodeVo();
        nodeVo.setNode(s);
        nodeVo.setRelation(s1);
        return nodeVo;
    }

    @GetMapping("/R1")
    @Log(title = "系统管理/图谱管理", businessType = BusinessType.QUERY)
    public NodeVo R1(){

        log.info("R1运行");


        Iterable<Root> all = rootRepository.findAll();
        Iterable<BD> all2 = bdRepository.findAll();
        List<Object> list = new ArrayList<>();
        for (Root root:all){
            list.add(root);
        }
        for (BD root:all2){
            list.add(root);
        }
        String s = this.node2Json(list);
        List<RelationShip> all1 = relationShipRepository.All();

        List<Object> list1 = new ArrayList<>();
        for (RelationShip relationShip:all1){
            list1.add(relationShip);
        }
        String s1 = this.relationShip2Json(list1);
        NodeVo nodeVo = new NodeVo();
        nodeVo.setNode(s);
        nodeVo.setRelation(s1);
        return nodeVo;
    }
    @GetMapping("/R2")
    public NodeVo R2(@RequestParam("id")Long id){
        List<RelationShip2> relationShip2s=  relationShip2Repository.getById(id);
        List<Object> list1 = new ArrayList<>();
        List<Object> list = new ArrayList<>();
        for (RelationShip2 relationShip:relationShip2s){
            list1.add(relationShip);
            list.add(relationShip.getModel());
        }
        String s1 = this.node2Json(list);
        String s = this.relationShip2Json(list1);
        NodeVo nodeVo = new NodeVo();
        nodeVo.setNode(s1);
        nodeVo.setRelation(s);
        return nodeVo;
    }

    @GetMapping("/R3")
    public NodeVo R3(@RequestParam("id")Long id){
        List<RelationShip3> relationShip3s=  relationShip3Repository.getById(id);
        List<Object> list1 = new ArrayList<>();
        List<Object> list = new ArrayList<>();
        for (RelationShip3 relationShip:relationShip3s){
            list1.add(relationShip);
            list.add(relationShip.getField());
        }
        String s1 = this.node2Json(list);
        String s = this.relationShip2Json(list1);
        NodeVo nodeVo = new NodeVo();
        nodeVo.setNode(s1);
        nodeVo.setRelation(s);
        return nodeVo;
    }

    @GetMapping("/R4")
    public NodeVo R4(@RequestParam("id")Long id){
        List<RelationShip4> relationShip4s=  relationShip4Repository.getById(id);
        List<Object> list1 = new ArrayList<>();
        List<Object> list = new ArrayList<>();
        for (RelationShip4 relationShip:relationShip4s){
            list1.add(relationShip);
            list.add(relationShip.getUpstream());
        }
        String s1 = this.node2Json(list);
        String s = this.relationShip2Json(list1);
        NodeVo nodeVo = new NodeVo();
        nodeVo.setNode(s1);
        nodeVo.setRelation(s);
        return nodeVo;
    }

    @GetMapping("/R5")
    public NodeVo R5(@RequestParam("id")Long id){
        List<RelationShip5> relationShip5s=  relationShip5Repository.getById(id);
        List<Object> list1 = new ArrayList<>();
        List<Object> list = new ArrayList<>();
        for (RelationShip5 relationShip:relationShip5s){
            list1.add(relationShip);
            list.add(relationShip.getUpstreamField());
        }
        String s1 = this.node2Json(list);
        String s = this.relationShip2Json(list1);
        NodeVo nodeVo = new NodeVo();
        nodeVo.setNode(s1);
        nodeVo.setRelation(s);
        return nodeVo;
    }

    @GetMapping("/model")
    public Object model(@RequestParam("id")Long id,@RequestParam("level") Integer level){
        System.out.println();
        if (level.equals(3)){
            return modelRepository.findById(id).get();
        }
        if (level.equals(4)){
            return fieldRepository.findById(id).get();
        }
        if (level.equals(5)){
            return upstreamRepository.findById(id).get();
        }
        if (level.equals(6)){
            return upstreamFieldRepository.findById(id).get();
        }
        if (level.equals(2)){
            return bdRepository.findById(id).get();
        }
        if (level.equals(1)){
            return rootRepository.findById(id).get();
        }
        return null;

    }

    @GetMapping("/byName")
    public NodeVo byName(@RequestParam("name")String name){
         List<RelationShip2> list=  relationShip2Repository.getByName(name);
         List<Object> r=new ArrayList<>();
         Set<Object> b=new HashSet<>();
         for (RelationShip2 relationShip2:list){
             r.add(relationShip2);
             b.add(relationShip2.getBd());
             b.add(relationShip2.getModel());
         }
        String s1 = this.node2Json(new ArrayList<>(b));

        String s = this.relationShip2Json(r);

        NodeVo nodeVo = new NodeVo();
        nodeVo.setNode(s1);
        nodeVo.setRelation(s);
        return nodeVo;
    }

    @org.springframework.beans.factory.annotation.Value("${neo4j.url}")
    private String url;

    @org.springframework.beans.factory.annotation.Value("${spring.data.neo4j.username}")
    private String username;

    @org.springframework.beans.factory.annotation.Value("${spring.data.neo4j.password}")
    private String password;

    @GetMapping("/params")
    public List<Object> params(@RequestParam("name")String name){
        if (StringUtils.isEmpty(name)){
            throw  new RuntimeException("不能为空");
        }
        Driver driver = GraphDatabase.driver("bolt://"+url, AuthTokens.basic(username, password));
        log.info("url:"+url);
        log.info("username:"+username);
        log.info("password:"+password);
        Session session = driver.session();
        String sql="MATCH (n) WHERE ANY(key IN keys(n) WHERE n[key] CONTAINS '"+name+"') RETURN n";
        Result run = session.run(sql);
        List<Record> list = run.list();
        List<Object> list1 = new ArrayList<>();


        for (int i=0;i<list.size();i++){
            List<Value> values =list.get(i).values();
            for (int j=0;j<values.size();j++){
                Map<String, Object> maps = new HashMap<>();
                Node node = values.get(j).asNode();

                Map<String, Object> map = node.asMap();
                maps.putAll(map);
                maps.put("id",node.id());
                list1.add(maps);

            }
        }
        return list1;
    }

    // todo 上传接口


    @GetMapping("/search")
    public NodeVo search(@RequestParam("id")Long id,@RequestParam("level") Integer level){
        NodeVo nodeVo = new NodeVo();
        if (level.equals(1)){
           Root root=  rootRepository.getById(id);
            List<Object> list = new ArrayList<>();
            list.add(root);
            String s = this.node2Json(list);
            nodeVo.setNode(s);
            return nodeVo;
        }else if (level.equals(2)){
            List<RelationShip> list= relationShipRepository.findByBdId(id);
            List<Object> list1=new ArrayList<>();
            List<Object> list2=new ArrayList<>();
            for (RelationShip relationShip:list){
                list1.add(relationShip.getRoot());
                list1.add(relationShip.getBd());
                list2.add(relationShip);
            }
            String s = this.node2Json(list1);
            String s1 = this.relationShip2Json(list2);
            nodeVo.setNode(s);
            nodeVo.setRelation(s1);
            return nodeVo;
        }else if (level.equals(3)){
            List<RelationShip2> listR2= relationShip2Repository.findByBdId(id);
            List<Object> list1=new ArrayList<>();
            List<Object> list2=new ArrayList<>();
            Long idBD=null;
            for (RelationShip2 relationShip:listR2){
                list1.add(relationShip.getModel());
                list1.add(relationShip.getBd());
                list2.add(relationShip);
                idBD=relationShip.getBd().getId();
            }

            List<RelationShip> listR1= relationShipRepository.findByBdId(idBD);
            for (RelationShip relationShip:listR1){
                list1.add(relationShip.getRoot());
                list1.add(relationShip.getBd());
                list2.add(relationShip);
            }

            Set<Object> objects = new HashSet<>(list1);
            Set<Object> object2 = new HashSet<>(list2);

            String s = this.node2Json(new ArrayList<>(objects));
            String s1 = this.relationShip2Json(new ArrayList<>(object2));
            nodeVo.setNode(s);
            nodeVo.setRelation(s1);
            return nodeVo;
        }else if (level.equals(4)){
            List<Object> list1=new ArrayList<>();
            List<Object> list2=new ArrayList<>();
            List<RelationShip3> listR3= relationShip3Repository.findByBdId(id);
            Long idModel=null;
            for (RelationShip3 relationShip:listR3){
                list1.add(relationShip.getModel());
                list1.add(relationShip.getField());
                list2.add(relationShip);
                idModel=relationShip.getModel().getId();
            }


            List<RelationShip2> listR2= relationShip2Repository.findByBdId(idModel);
            Long idBD=null;
            for (RelationShip2 relationShip:listR2){
                list1.add(relationShip.getModel());
                list1.add(relationShip.getBd());
                list2.add(relationShip);
                idBD=relationShip.getBd().getId();
            }
            List<RelationShip> listR1= relationShipRepository.findByBdId(idBD);
            for (RelationShip relationShip:listR1){
                list1.add(relationShip.getRoot());
                list1.add(relationShip.getBd());
                list2.add(relationShip);
            }

            Set<Object> objects = new HashSet<>(list1);
            Set<Object> object2 = new HashSet<>(list2);

            String s = this.node2Json(new ArrayList<>(objects));
            String s1 = this.relationShip2Json(new ArrayList<>(object2));
            nodeVo.setNode(s);
            nodeVo.setRelation(s1);
            return nodeVo;
    }else if (level.equals(5)){
            List<Object> list1=new ArrayList<>();
            List<Object> list2=new ArrayList<>();
            List<RelationShip4> listR4= relationShip4Repository.findByBdId(id);
            Long  idField=null;
            for (RelationShip4 relationShip:listR4){
                list1.add(relationShip.getUpstream());
                list1.add(relationShip.getField());
                list2.add(relationShip);
                idField=relationShip.getField().getId();
            }

            List<RelationShip3> listR3= relationShip3Repository.findByBdId(idField);
            Long idModel=null;
            for (RelationShip3 relationShip:listR3){
                list1.add(relationShip.getModel());
                list1.add(relationShip.getField());
                list2.add(relationShip);
                idModel=relationShip.getModel().getId();
            }

            List<RelationShip2> listR2= relationShip2Repository.findByBdId(idModel);
            Long idBD=null;
            for (RelationShip2 relationShip:listR2){
                list1.add(relationShip.getModel());
                list1.add(relationShip.getBd());
                list2.add(relationShip);
                idBD=relationShip.getBd().getId();
            }
            List<RelationShip> listR1= relationShipRepository.findByBdId(idBD);
            for (RelationShip relationShip:listR1){
                list1.add(relationShip.getRoot());
                list1.add(relationShip.getBd());
                list2.add(relationShip);
            }
            Set<Object> objects = new HashSet<>(list1);
            Set<Object> object2 = new HashSet<>(list2);

            String s = this.node2Json(new ArrayList<>(objects));
            String s1 = this.relationShip2Json(new ArrayList<>(object2));
            nodeVo.setNode(s);
            nodeVo.setRelation(s1);
            return nodeVo;
   }
        return null;
    }



    private  Map<String,List<Object>> model(Long id){
        Map<String,List<Object>> map = new HashMap<>();
        List<RelationShip2> list= relationShip2Repository.findByBdId(id);
        List<Object> list1=new ArrayList<>();
        List<Object> list2=new ArrayList<>();
        for (RelationShip2 relationShip:list){
            list1.add(relationShip.getModel());
            list1.add(relationShip.getBd());
            list2.add(relationShip);
        }
        map.put("node",list1);
        map.put("r",list2);

        return map;
    }


    public String relationShip2Json( List<Object> list){
        List<Map<String,String>> maps=new ArrayList<>();
        for (Object relationShip:list){
            Map<String,String> map = new HashMap<>();
            map.put("from",((BaseR)relationShip).getFromId().toString());
            map.put("text",((BaseR)relationShip).getName());
            map.put("to",((BaseR)relationShip).getToId().toString());
            maps.add(map);
        }
        return new JSONArray(maps).toString();
    }



    public String node2Json(List<Object> roots){
        List<Map<String,String>> maps=new ArrayList<>();
        for (Object root:roots){
            Map<String,String> map = new HashMap<>();
            map.put("text",((BaseEntity)root).getName());
            map.put("level",((BaseEntity)root).getLevel().toString());
            map.put("id",((BaseEntity)root).getId().toString());
            maps.add(map);
        }
        return new JSONArray(maps).toString();
    }


}
